package crontask

import (
    "github.com/astaxie/beego/logs"
    "fmt"
    "strings"
    "net"
    "errors"
    "time"
    "cloud_worm/util/umodels/mysqldb"
    "cloud_worm/util/uweb/ucron"
    "cloud_worm/util/umodels/mysqldb/dbmanager"
    "cloud_worm/util/umodels/mysqldb/dbtask"
    "cloud_worm/util/uhttp"
    "cloud_worm/wormweb/controllers/ctrconfig"
    "cloud_worm/wormweb/controllers/ctrutil"
)

type CronController struct {
    ctrconfig.LoginCtr
}

/**
 @Description：创建一个cron task的对象
    两种任务：
        1. 其他类型的任务: 特性字段为：tasktype=2，target=xxxx为预定义涉及好的Job;
        2. weapon相关的任务：特性字段为：tasktype=1，Target=xxxx为目标，WeaponName=xxx为相关的weapon;
 */
func (c *CronController) CreateCron()  {
    c.Manager()

    type InputInfo struct {
        Name        string        `json:"name"`
        Spec        string        `json:"spec"`
        TaskType    int           `json:"task_type"`
        Weapon      string        `json:"weapon"`
        Target      string        `json:"target"`
    }
    var input InputInfo
    if err := c.Unmarshal(&input); err != nil {
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
        c.ServeJSON()
        return
    }

    // 任务名，task name
    if input.Name == mysqldb.SelectStrAll {
        logs.Error(fmt.Sprintf("name input empty."))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "name input error.", nil)
        c.ServeJSON()
        return
    }
    // 任务执行时间
    err := CheckSpec(input.Spec)
    if err != nil {
        logs.Error(fmt.Sprintf("spec input error: %s", err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("spec input error: %s", err.Error()), nil)
        c.ServeJSON()
        return
    }
    // 任务类型
    if input.TaskType != CronWeaponTask && input.TaskType != CronOtherTask {
        logs.Error(fmt.Sprintf("task_type input error: %s", err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "task_type input error", nil)
        c.ServeJSON()
        return
    }

    var wps string
    if input.TaskType == CronWeaponTask {
        // 如果是weapon的任务，则weapon下发任务，则解析weapon，检查weapon的输入
        wps, err = c.checkInputWeapon(input.Weapon)
        if err != nil {
            logs.Error(fmt.Sprintf("input weapon name error: %s", err.Error()))
            c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "weapon name input error", nil)
            c.ServeJSON()
            return
        }
    }

    // 目标target
    if input.Target == mysqldb.SelectStrAll {
        logs.Error(fmt.Sprintf("target input empty."))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "target input error", nil)
        c.ServeJSON()
        return
    }
    targetType, err := c.parseTarget(input.TaskType, input.Target)
    if err != nil {
        logs.Error(fmt.Sprintf("target input error: %s", err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("target input error: %s", err.Error()), nil)
        c.ServeJSON()
        return
    }

    // 唯一性检查
    ct := &dbtask.CronTask{Name: input.Name}
    err = ct.Read("name")
    if err == nil {
        logs.Error(fmt.Sprintf("task `%s` is already exist.", input.Name))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("task `%s` is already exist.", input.Name), nil)
        c.ServeJSON()
        return
    }
    ct.TaskOwner = c.UserInfo.Username
    ct.TaskType = input.TaskType
    ct.Spec = input.Spec
    ct.TargetType = targetType
    ct.Target = input.Target
    ct.WeaponsName = wps
    ct.Created = time.Now().Unix()
    ct.Updated = ct.Created
    ct.Status = mysqldb.StatusInvalid
    err = ct.Insert()
    if err != nil {
        logs.Error(fmt.Sprintf("task `%s` insert error: %s", input.Name, err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("task `%s` insert error: %s", input.Name, err.Error()), nil)
        c.ServeJSON()
        return
    }

    c.Data["json"] = uhttp.CreateOkResponse(nil)
    c.ServeJSON()
    return
}

// target的分析处理
func (c *CronController) parseTarget(taskType int, target string) (int, error) {
    if taskType == CronWeaponTask {
        // weapon task对于target进行分析
        // <all>
        if target == ctrutil.TaskAllApplication {
            return ctrutil.TaskForAll, nil
        }

        ip := net.ParseIP(target)
        if ip == nil {
            // 不是ip，则匹配application name
            app := &dbmanager.Application{Name: target}
            err := app.Read("name")
            if err != nil {
                // 应用不存在，返回错误
                return 0, err
            }
            // 匹配到application name
            return ctrutil.TaskForApplication, nil
        }

        machine := &dbmanager.Machine{Ip: target}
        err := machine.Read("ip")
        if err != nil {
            // ip，则匹配机器，没有匹配则返回err
            return 0, err
        }
        return ctrutil.TaskForMachine, nil

    } else if taskType == CronOtherTask {
        // 其他任务的target的任务分析
        _, exist := ucron.GetJob(target)
        if exist == false {
            return 0, errors.New(fmt.Sprintf("input target type error: %s", target))
        }

        return 0, nil                       // other的cron，其中的target type是无意义的
    } else {
        return 0, errors.New(fmt.Sprintf("input target type error: %s", taskType))
    }
}

// 输入的weapon的检查
func (c *CronController) checkInputWeapon(wps string) (string, error) {
    var elems []string
    tmp := strings.Split(wps, ",")
    for _, item := range tmp {
        one := strings.TrimSpace(item)
        if len(one) <= 0 {
            continue
        }

        elems = append(elems, one)
    }

    if len(elems) == 0 {
        return "", fmt.Errorf("input weapon name is empty")
    }

    return strings.Join(elems, ","), nil
}

// 获取所有预定义
func (c *CronController) AllJobModeName() {
    c.Manager()

    jobs := ucron.GetAllJob()
    data := map[string]interface{}{"total": len(jobs), "data": jobs}
    c.Data["json"] = uhttp.CreateOkResponse(data)
    c.ServeJSON()
    return
}

/**
 @Description：更新cron task，涉及到task的编辑
    1. task必须存在；
    2. task的状态必须为invalid的情况下，才能edit
 */
func (c *CronController) EditCron()  {
    c.Manager()

    type InputInfo struct {
        Id      int64         `json:"id"`
        Name    string        `json:"name"`
        Spec    string        `json:"spec"`
        TaskType    int       `json:"task_type"`
        Weapon  string        `json:"weapon"`
        Target  string        `json:"target"`
    }
    var input InputInfo
    if err := c.Unmarshal(&input); err != nil {
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
        c.ServeJSON()
        return
    }

    ct := &dbtask.CronTask{Id: input.Id}               // 存在性检查
    if err := ct.Read("id"); err != nil {
        logs.Error(fmt.Sprintf("task `%d` is not exist.", input.Id))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("task `%d` is not exist.", input.Id), nil)
        c.ServeJSON()
        return
    }
    if ct.Status != mysqldb.StatusInvalid {             // 保证是invalid的情况下，才能编辑
        logs.Error(fmt.Sprintf("task `%d` is valid, can't edit.", input.Id))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("task `%s` is valid, can't edit.", input.Id), nil)
        c.ServeJSON()
        return
    }

    if input.Name == mysqldb.SelectStrAll {
        logs.Error(fmt.Sprintf("task name input error."))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("task name input error."), nil)
        c.ServeJSON()
        return
    }
    // 任务执行时间
    if err := CheckSpec(input.Spec); err != nil {
        logs.Error(fmt.Sprintf("spec input error: %s", err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("spec input error: %s", err.Error()), nil)
        c.ServeJSON()
        return
    }
    if input.TaskType != CronOtherTask && input.TaskType != CronWeaponTask {
        logs.Error(fmt.Sprintf("task_type input error: %d", input.TaskType))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "task_type input error", nil)
        c.ServeJSON()
        return
    }

    var wps string
    if input.TaskType == CronWeaponTask {
        // 如果是weapon的任务，则weapon下发任务，则解析weapon，检查weapon的输入
        var err error
        wps, err = c.checkInputWeapon(input.Weapon)
        if err != nil {
            logs.Error(fmt.Sprintf("input weapon name error: %s", err.Error()))
            c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "weapon name input error", nil)
            c.ServeJSON()
            return
        }
    }

    // 目标target
    if input.Target == mysqldb.SelectStrAll {
        logs.Error(fmt.Sprintf("target input empty."))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "target input error", nil)
        c.ServeJSON()
        return
    }
    targetType, err := c.parseTarget(input.TaskType, input.Target)
    if err != nil {
        logs.Error(fmt.Sprintf("target input error: %s", err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("target input error: %s", err.Error()), nil)
        c.ServeJSON()
        return
    }

    // 执行更新
    ct.Name = input.Name
    ct.TaskOwner = c.UserInfo.Username
    ct.TaskType = input.TaskType
    ct.Spec = input.Spec
    ct.TargetType = targetType
    ct.Target = input.Target
    ct.WeaponsName = wps
    ct.Updated = time.Now().Unix()
    err = ct.Update("name", "task_owner", "task_type", "spec", "target_type", "target", "weapons_name", "updated")
    if err != nil {
        logs.Error(fmt.Sprintf("cron task `%d` update error: %s", input.Id, err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("cron task `%d` update error: %s", input.Id, err.Error()), nil)
        c.ServeJSON()
        return
    }

    c.Data["json"] = uhttp.CreateOkResponse(nil)
    c.ServeJSON()
    return
}

// 生效和去生效，cron task
func (c *CronController) UpdateCron()  {
    c.Manager()

    type InputInfo struct {
        Id      int64         `json:"id"`
        Status  int           `json:"status"`
    }
    var input InputInfo
    if err := c.Unmarshal(&input); err != nil {
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeReqBodyErr, err.Error(), nil)
        c.ServeJSON()
        return
    }
    if input.Status != mysqldb.StatusInvalid && input.Status != mysqldb.StatusValid {
        logs.Error(fmt.Sprintf("task `%d` status input error: %d", input.Id, input.Status))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("task `%d` status input error: %d", input.Id, input.Status), nil)
        c.ServeJSON()
        return
    }

    ct := &dbtask.CronTask{Id: input.Id}               // 存在性检查
    if err := ct.Read("id"); err != nil {
        logs.Error(fmt.Sprintf("task `%d` is not exist.", input.Id))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("task `%d` is not exist.", input.Id), nil)
        c.ServeJSON()
        return
    }
    // 没变化，则不动
    if ct.Status == input.Status {
        c.Data["json"] = uhttp.CreateOkResponse(nil)
        c.ServeJSON()
        return
    }

    tmp := &CrWeaponTask{
        Id: ct.Id,
        Name: ct.Name,
        TaskOwner: ct.TaskOwner,
        TaskType: ct.TaskType,
        Spec: ct.Spec,
        TargetType: ct.TargetType,
        Target: ct.Target,
        WeaponsName: ct.WeaponsName,
        Created: ct.Created,
        Updated: ct.Updated,
        Status: ct.Status,
    }
    // 根据不同状态处理
    if input.Status == mysqldb.StatusInvalid {
        // 不生效处理，从cron中移除
        RemoveCronJob(tmp)
    } else if input.Status == mysqldb.StatusValid {
        // 生效处理
        err := AddCronJob(tmp)
        if err != nil {
            logs.Error(fmt.Sprintf("task `%d` add cron failed: %s.", input.Id, err.Error()))
            c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("task `%d` add cron failed: %s.", input.Id, err.Error()), nil)
            c.ServeJSON()
            return
        }
    } else {
        logs.Error(fmt.Sprintf("task `%d` status input error: %d", input.Id, input.Status))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, fmt.Sprintf("task `%d` status input error: %d", input.Id, input.Status), nil)
        c.ServeJSON()
        return
    }

    // 更新表数据
    ct.Status = input.Status
    ct.Updated = time.Now().Unix()
    if err := ct.Update("status", "updated"); err != nil {
        logs.Error(fmt.Sprintf("cron task `%d` update error: %s", input.Id, err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("cron task `%d` update error: %s", input.Id, err.Error()), nil)
        c.ServeJSON()
        return
    }

    c.Data["json"] = uhttp.CreateOkResponse(nil)
    c.ServeJSON()
    return
}

// 展示所有的cron task计划任务
func (c *CronController) List()  {
    c.Manager()

    offset, limit := c.DealPage()

    // apply状态
    status, err := c.GetInt("status", mysqldb.StatusAll)
    name := c.GetString("name", mysqldb.SelectStrAll)
    owner := c.GetString("owner", mysqldb.SelectStrAll)
    taskType, err := c.GetInt("type", mysqldb.SelectIntAll)

    ct := &dbtask.CronTask{}
    count, err := ct.Count(status, name, owner, taskType)
    if err != nil {
        data := map[string]interface{}{"total": 0, "data": []dbtask.CronTask{}}
        logs.Error(fmt.Sprintf("cron task count error: %s", err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("cron task count error: %s", err.Error()), data)
        c.ServeJSON()
        return
    }

    cts, err := ct.Select(offset, limit, status, name, owner, taskType)
    if err != nil {
        data := map[string]interface{}{"total": 0, "data": []dbtask.CronTask{}}
        logs.Error(fmt.Sprintf("cron task select error: %s", err.Error()))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("cron task select error: %s", err.Error()), data)
        c.ServeJSON()
        return
    }

    data := map[string]interface{}{"total": count, "data": cts}
    c.Data["json"] = uhttp.CreateOkResponse(data)
    c.ServeJSON()
    return
}

// cron task执行的记录
func (c *CronController) TaskDoDetail()  {
    c.Manager()

    //offset, limit := c.DealPage()

    id, err := c.GetInt64("id", mysqldb.SelectIntAll)
    if err != nil || id == mysqldb.SelectIntAll {
        logs.Error(fmt.Sprintf("id input empty."))
        c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeParamErr, "id input error.", nil)
        c.ServeJSON()
        return
    }

    // todo：定时任务执行的记录
    //doCnt := &dbtask.CronTaskDoCount{}
    //count, err := doCnt.Count(id)
    //if err != nil {
    //    data := map[string]interface{}{"total": 0, "data": []dbdata.WeakConnectDetail{}}
    //    logs.Error(fmt.Sprintf("cron do detail count error: %s", err.Error()))
    //    c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("cron do detail count error: %s", err.Error()), data)
    //    c.ServeJSON()
    //    return
    //}
	//
    //cts, err := doCnt.Select(offset, limit, id)
    //if err != nil {
    //    data := map[string]interface{}{"total": 0, "data": []dbdata.WeakConnectDetail{}}
    //    logs.Error(fmt.Sprintf("cron do detail select error: %s", err.Error()))
    //    c.Data["json"] = uhttp.CreateErrResponse(uhttp.RespCodeSystemErr, fmt.Sprintf("cron do detail select error: %s", err.Error()), data)
    //    c.ServeJSON()
    //    return
    //}

    data := map[string]interface{}{"total": 10, "data": nil}
    c.Data["json"] = uhttp.CreateOkResponse(data)
    c.ServeJSON()
    return
}


// 进程启动的时候，加载cron task
func InitLoadCronTask() error {
    ct := &dbtask.CronTask{}
    cts, err := ct.Select(mysqldb.SelectOffsetZero, mysqldb.SelectLimitAll, mysqldb.StatusValid, mysqldb.SelectStrAll,
        mysqldb.SelectStrAll, mysqldb.SelectIntAll)
    if err != nil {
        return err
    }

    for _, one := range cts {

        tmp := &CrWeaponTask{
            Id: one.Id,
            Name: one.Name,
            TaskOwner: one.TaskOwner,
            TaskType: one.TaskType,
            Spec: one.Spec,
            TargetType: one.TargetType,
            Target: one.Target,
            WeaponsName: one.WeaponsName,
            Created: one.Created,
            Updated: one.Updated,
            Status: one.Status,
        }
        err = AddCronJob(tmp)
        if err != nil {
            logs.Error(fmt.Sprintf("task `%s` add cron failed: %s.", one.Name, err.Error()))
        } else {
            logs.Info(fmt.Sprintf("task `%s` add cron.", one.Name))
        }
    }
    logs.Info("init load cron task.")

    return nil
}
